package Q1;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.query.Query;
import org.junit.Before;
import org.junit.Test;
import utils.FileUtils;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.sql.rowset.serial.SerialBlob;
import java.io.IOException;
import java.sql.Date;
import java.sql.SQLException;
import java.util.List;

/**
 * 通过 Hibernate 测试学生表的 CRUD
 *
 * @author chisheng
 * @since 2021-10-25
 */
public class HibernateDemo {

    /**
     * Hibernate 会话工厂
     */
    private SessionFactory factory;

    /**
     * 测试初始化，主要完成 SessionFactory 的赋值
     */
    @Before
    public void initialize() {
        factory = HibernateConfig.getSessionFactory();
    }

    /**
     * 测试插入一个学生到学生表中
     * 1) 通过 openSession() 方法获取一个 Session 对象
     * 2) 构造一个需要插入到数据库中的学生对象
     * 3) 通过 beginTransaction() 方法开启事务
     * 4) 通过 save(Object object) 方法插入目标学生对象
     * 5) 通过 commit() 提交事务
     */
    @Test
    public void testInsert() throws IOException, SQLException {
        try (Session session = factory.openSession() /* 获取一个 Session 对象 */) {
            /* 构造一个需要插入到数据库中的学生对象 */
            SerialBlob photo = new SerialBlob(FileUtils.getFileBytes(FileUtils.getClasspathFile("photo.jpg")));
            HibernateStudent insertedHibernateStudent = new HibernateStudent(
                    1, "201901", "张三", 18, Date.valueOf("2000-12-05"), photo
            );

            /* 开启事务 */
            Transaction transaction = session.beginTransaction();

            /* 插入目标学生 */
            session.save(insertedHibernateStudent);

            /* 提交事务 */
            transaction.commit();
        }
    }

    /**
     * 测试查询表中的所有学生
     * 1) 通过 openSession() 方法获取一个 Session 对象
     * 2) 通过 createQuery(String queryString, Class<T> resultType) 方法进行查询
     * 3) 通过 list() 方法获取查询到的列表
     */
    @Test
    public void testSelect() {
        try (Session session = factory.openSession() /* 获取一个 Session 对象 */) {
            /* 通过 HQL 查询所有学生 */
            Query<HibernateStudent> studentQuery = session.createQuery("from HibernateStudent", HibernateStudent.class);
            /* 通过 list() 获取查询到的学生列表 */
            List<HibernateStudent> hibernateStudentList = studentQuery.list();
            /* 输出查询到的数据 */
            for (HibernateStudent hibernateStudent : hibernateStudentList) {
                System.out.println(hibernateStudent);
            }
        }
    }

    /**
     * 测试查询表中的指定学生
     * 1) 通过 openSession() 方法获取一个 Session 对象
     * 2) 通过 session.getEntityManagerFactory().createEntityManager() 获取实体管理器
     * 3) 通过 session.getCriteriaBuilder() 获取条件查询构建器
     * 4) 通过 createQuery(Class<T> resultClass) 创建定义学生实体的查询
     * 5) 定义 Predicate 查询条件
     * 6) 通过 manager.createQuery(CriteriaQuery<T> criteriaQuery) 获取查询结果
     */
    @Test
    public void testSpecificSelect() {
        try (Session session = factory.openSession() /* 获取一个 Session 对象 */) {
            /* 可类似 testSelect() 演示方式，通过拼接 SQL 语句 */
            // Query<Student> studentQuery = session.createQuery("from Student where id=" + 1, Student.class);

            /* 若通过主键查询，可通过 find(Class<T> entityClass, Object primaryKey) 方法 */
            // Student student = session.find(Student.class, 1);

            /* 获取实体管理器 */
            EntityManager manager = session.getEntityManagerFactory().createEntityManager();
            /* 获取条件查询构建器 */
            CriteriaBuilder builder = session.getCriteriaBuilder();
            /* 定义学生实体的查询 */
            CriteriaQuery<HibernateStudent> query = builder.createQuery(HibernateStudent.class);
            /* from() 类似于 SQL 中的 from 语句；from Student */
            Root<HibernateStudent> root = query.from(HibernateStudent.class);
            /* 定义查询条件；该 predicate 也就是在 where 后面的条件子句 */
            Predicate predicate = builder.equal(root.get("id"), 1);
            query.where(predicate);
            /* 执行查询 */
            List<HibernateStudent> queryList = manager.createQuery(query).getResultList();
            /* 输出查询结果 */
            System.out.println(queryList);
        }
    }

    /**
     * 测试修改学生表的学生记录
     * 1) 通过 openSession() 方法获取一个 Session 对象
     * 2) 通过 find(Class<T> entityClass, Object primaryKey) 方法从数据库中查询出需要修改的学生
     * 3) 修改上一步查询出的实例
     * 4) 通过 beginTransaction() 开启事务
     * 5) 通过 update(Object object) 方法更新数据库字段
     * 6) 通过 commit() 提交事务
     */
    @Test
    public void testUpdate() {
        try (Session session = factory.openSession() /* 获取一个 Session 对象 */) {
            /* 从数据库中查询出需要修改的记录，之所以这么做是因为修改是修改所有字段，因此未修改字段必须是原来的初始值 */
            HibernateStudent hibernateStudent = session.find(HibernateStudent.class, 1);
            /* 在查询到的原学生实例上进行修改 */
            hibernateStudent.setName("李四");
            /* 开启事务 */
            Transaction transaction = session.beginTransaction();
            /* 更新学生记录 */
            session.update(hibernateStudent);
            /* 提交事务 */
            transaction.commit();
        }
    }

    /**
     * 测试删除学生表中的记录
     * 1) 通过 openSession() 方法获取一个 Session 对象
     * 2) 构建出需要被删除学生实例对象，实例字段必须被赋值
     * 3) 通过 beginTransaction() 开启事务
     * 4) 通过 delete(Object object) 删除指定学生记录
     * 5) 通过 commit() 提交事务
     */
    @Test
    public void testDelete() {
        try (Session session = factory.openSession() /* 获取一个 Session 对象 */) {
            /* 构建出需要被删除学生实例对象，实例字段必须被赋值 */
            HibernateStudent hibernateStudent = new HibernateStudent(1);
            /* 开启事务 */
            Transaction transaction = session.beginTransaction();
            /* 删除学生记录 */
            session.delete(hibernateStudent);
            /* 提交事务 */
            transaction.commit();
        }
    }

}
